Skip to content

fix(classify): cross-address-family mapping verdict (closes #14) — v0.1.3#18

Merged
vnykmshr merged 3 commits intomainfrom
release/v0.1.3-cross-family-fix
Apr 26, 2026
Merged

fix(classify): cross-address-family mapping verdict (closes #14) — v0.1.3#18
vnykmshr merged 3 commits intomainfrom
release/v0.1.3-cross-family-fix

Conversation

@vnykmshr
Copy link
Copy Markdown
Contributor

@vnykmshr vnykmshr commented Apr 26, 2026

Closes #14.

internal/classify/classify.go: group successes by Mapped.Addr().Is4(), classify each group via extracted classifyGroup, combine via combineGroups under "Unknown is absence of information, not disagreement." New mixed_address_family_probes warning constant (additive to warnings[]). No other schema delta.

User impact: cross-family probe sets that previously emitted nat_type: ADM now emit Unknown (or the agreed verdict when both families match). Forecast-checking consumers (webrtc_forecast.direct_p2p) mostly unaffected — Unknown still exits 1.

3 atomic commits: classifier change / 7-case test addition / CHANGELOG + design.md. Coverage 95.4%. make test/lint/gofmt/govulncheck clean.

Post-merge: re-validate against droplet with natural invocation (stun.l.google.com:19302 + stun.cloudflare.com:3478 + 168.144.121.96:3478); tag v0.1.3.

Closes #14.

Probes from a dual-stack network observe TWO different NATs (v4 NAT
and v6 router are independent). Previously the classifier compared
mapped endpoints across all successful probes for equality — cross-
family endpoints can never match by construction, so any v4-literal
+ v6-resolved-hostname probe set was misclassified as ADM/symmetric
(typical case: --server stun.l.google.com:19302 +
--server <coturn-ipv4>:3478 on dual-stack network).

Rework: classifyMapping now groups successful probes by
Mapped.Addr().Is4(), classifies each group via the extracted
classifyGroup helper, then combines via combineGroups under the rule
"Unknown is absence of information, not disagreement":
  - both groups confident and matching → use that mapping
  - both confident and disagreeing → combined Unknown
  - one confident + one Unknown → use the confident one
  - both Unknown → combined Unknown

CGNAT is OR'd across groups (decideForecast's CGNAT precedence then
overrides forecast to Unknown). New WarnMixedAddressFamilyProbes
warning added to the vocabulary, fires whenever ≥2 families present.
Existing WarnADMOrStricter is suppressed under disagreement-Unknown
(premise no longer holds). Other warnings (cgnat_detected,
insufficient_probes) propagate independently from any group.

PublicEndpoint top-level field unchanged (= first success's mapped
endpoint, regardless of family). probes[] array already shows all
families' endpoints. JSON schema delta is additive (new warning value).
7 new TestClassify_MixedAddressFamily cases, one per combine branch:

- mixed_v4eim_v6eim_agreed         → EIM (both groups confident, match)
- mixed_v4eim_v6adm_disagreement   → Unknown (both confident, differ)
- mixed_v4eim_v6singleton_v4_wins  → EIM (Unknown isn't disagreement)
- mixed_v4cgnat_eim_v6eim          → EIM mapping, forecast Unknown
                                     (CGNAT precedence in decideForecast)
- mixed_singleton_each             → Unknown (both groups inconclusive)
- single_family_v6_only            → unchanged path, no mixed warning
- single_family_v4_unchanged_path  → regression guard for existing v4 path

Plus: TestClassify_FilteringMatrix gains a final assertion that
WarnMixedAddressFamilyProbes never leaks in (matrix uses single-server
single-family probes).

Coverage stays at 95.4%.
CHANGELOG: [0.1.3] entry under Fixed (cross-family mapping bug),
Added (mixed_address_family_probes warning), with migration note for
JSON consumers and a note on the v0.1.2.x 4-segment-tag incident.

design.md addendum: v0.1.3 marked shipped (cross-family fix), v0.1.2.x
row added (asset patches between v0.1.2 and v0.1.3), hairpinning
shifted to v0.1.4, natcheck server to v0.1.5. v0.2.0 line unchanged.
@vnykmshr
Copy link
Copy Markdown
Contributor Author

vnykmshr commented Apr 26, 2026

Code review

No issues found.

@vnykmshr vnykmshr merged commit ae2c605 into main Apr 26, 2026
5 checks passed
@vnykmshr vnykmshr deleted the release/v0.1.3-cross-family-fix branch April 26, 2026 11:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

classifier: cross-address-family mapped endpoints produce wrong ADM verdict

1 participant